home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
bit
/
src
/
browse.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
45KB
|
1,921 lines
/*
* $Id: browse.c,v 0.91 1994/02/20 00:53:29 zhao Pre-Release $
*
*. This file is part of BIT shareware package. After the two weeks of
* free evaluation period, you are encouraged (required) to register
* your copy for a small registration fee, which is $35 for personal use
* and $50 for commercial, government and institutional use.
*
* Copyright(c) 1993, 1994 by T.C. Zhao.
* All rights reserved.
*
* Permission to use, copy, and distribute this software in its entirety
* for non-commercial purposes is hereby granted, provided that the
* above shareware and copyright notices and this permission notice
* appear in all copies and their documentation.
*
* This software may be modified for your own use, but modified versions
* may not be distributed without prior consent of the author.
*
* This software is provided "as is" without expressed or implied
* warranty of any kind.
*
*.
*
* Image browser. The thumbnail size can be 80x60 (default) or 100x80.
* Define S100x80 to get 100x80.
*
*/
#if !defined(lint) && defined(F_ID)
char *id_ibrw = "$Id: browse.c,v 0.91 1994/02/20 00:53:29 zhao Pre-Release $";
#endif
#include "bit.h"
#include "dmalloc.h"
#include "extern.h"
/**************** Limits and defines **************************/
#define MAXDIR 7 /* max. simultanou dirs */
#define MAXTNI 15 /* max. icons per screen */
#define TNIQ 2 /* thumbnail image quality 0-2 */
#define MAXFLSHOWN 16 /* thumbnail label limit */
#ifdef S100x80
#define ICON_W 100.0 /* thumbnail image width */
#define ICON_H 80.0 /* thumbnail image height */
#else
#define ICON_W 80.0
#define ICON_H 60.0
#endif
#define THUMBDIR ".btn/" /* thumbnail directory */
#define THUMBSIG "BTN" /* signatures */
/* due to re-entrant problems, need to block activaties while loading */
#define IB_BLOCK fl_deactivate_all_forms()
#define IB_UNBLOCK fl_activate_all_forms()
/*************** Structure and its associated functions ******/
typedef struct thumb_
{
struct thumb_ *next; /* link */
char *name; /* original image name. */
char *info; /* original image dimension info */
char *ifmt; /* short format info */
IPTR im; /* the thumbnail image */
int selected; /* true if marked */
}
Thumb_t;
static Thumb_t *ithumb[MAXDIR], *itail[MAXDIR];
static int itotal[MAXDIR];
static IPTR dir_image, unknown;
/* check if a thumbnail is a directory if no associated icon, t->im==0 */
#define T_ISDIR(t) (t->info || (t->ifmt && strcmp(t->ifmt,"Dir")==0))
/*******************************************************************
* Allocate a thumbnail image structure and initialize
******************************************************************/
/* Asuuming null pointers have all bits zero, we can save some time */
#if 1
#define ithumb_alloc() calloc(1, sizeof(Thumb_t));
#else
/* The function version of ithumb_alloc */
static Thumb_t *
ithumb_alloc(void)
{
Thumb_t *t = malloc(sizeof(*t));
if (t)
{
t->next = 0;
t->name = t->info = t->ifmt = 0;
t->im = 0;
t->selected = 0;
}
return t;
}
#endif /* Use alloc macros */
/********************************************************************
* Free a thumbnail image
********************************************************************/
/* free the raster and its associated info */
static void
free_thumbnail_raster(Thumb_t * t)
{
free_image(t->im);
t->im = 0;
if (t->info)
{
free(t->info);
t->info = 0;
}
}
/* free all memeory associatd with a thumbnail */
static void
ithumb_free(Thumb_t * t)
{
free_thumbnail_raster(t);
if (t->name)
free(t->name);
t->name = 0;
if (t->ifmt)
free(t->ifmt);
t->ifmt = 0;
free(t);
}
/*********************************************************************
* Remove a thumbnail image from the list
*********************************************************************/
static void
ithumb_del(register Thumb_t * t, register int br)
{
register Thumb_t *p = ithumb[br], *q;
if (p == t)
{
ithumb[br] = p->next;
if (itail[br] == t)
itail[br] = 0;
}
else
{
for (q = p; q && q->next != t; q = q->next)
;
if (!q)
return;
q->next = q->next->next;
if (t == itail[br])
itail[br] = q;
}
ithumb_free(t);
--itotal[br];
}
/*******************************************************************
* Free all icon images, and leave original filename etc intact
*******************************************************************/
static void
free_ibrowser_thumbnail_image(int br)
{
Thumb_t *p;
for (p = ithumb[br]; p; p = p->next)
free_thumbnail_raster(p);
}
/********************************************************************
* Free the whole list
*******************************************************************/
static void
free_ibrowser(int br)
{
Thumb_t *p, *q;
for (p = ithumb[br]; p; p = q)
{
q = p->next;
ithumb_free(p);
}
ithumb[br] = itail[br] = 0;
itotal[br] = 0;
}
/*******************************************************************
* Free all memeory used by image browsers
*******************************************************************/
void
free_all_ibrowsers(void)
{
int i;
for (i = 0; i < MAXDIR; i++)
free_ibrowser(i);
free_image(dir_image);
dir_image = 0;
free_image(unknown);
unknown = 0;
}
/*********************************************************************
* Append one at end
*********************************************************************/
static void
append_ibrowser(int br, Thumb_t * t)
{
if (!ithumb[br])
{
ithumb[br] = itail[br] = t;
++itotal[br];
return;
}
itail[br]->next = t;
itail[br] = t;
++itotal[br];
}
/**********************************************************************
* Local variables
*********************************************************************/
static FL_FORM *ibrowser[MAXDIR];
static FL_OBJECT *icons[MAXDIR][MAXTNI], *title[MAXDIR], *nfiles[MAXDIR];
static FL_OBJECT *dnbox[MAXDIR], *sld[MAXDIR], *pager[MAXDIR];
static char *lastdir[MAXDIR];
static char *pat = "*";
static int offset[MAXDIR];
static int tni_quality = TNIQ;
static int fitexact; /* bad idea to to fit exact */
static char complete_dir[PATH_MAX];
/**************** Local function (forward) ************/
#ifdef S100x80
static void create_browser_100x80(int);
#else
static void create_browser_80x60(int);
#endif
static void clean_thumbnail_dir(const char *, int);
static void make_complete_dir(const char *, char *);
/**********************************************************************
* get an available index. If no space left, free one
***********************************************************************/
static int
alloc_browser(const char *dir, int *br)
{
static int lastb;
int i = 0, cached = 0;
do
{
cached = (lastdir[i] && strcmp(lastdir[i], dir) == 0);
}
while (!cached && ++i < MAXDIR);
if (!cached)
{
StrReDup(lastdir[lastb], dir);
i = lastb;
lastb = (lastb + 1) % MAXDIR;
}
*br = (i >= MAXDIR) ? -1 : i;
return cached;
}
/**********************************************************************
* Write a thumbnail image to disk
*********************************************************************/
static void
ithumb_dump(Thumb_t * t)
{
char buf[1024];
strcat(strcpy(buf, THUMBDIR), t->im->ifile);
M_info("IThunmGen", "Writing %s", buf);
if ((t->im->fp = fopen(buf, "w")))
{
fprintf(t->im->fp, "%s\n%s\n%s\n", THUMBSIG, t->name, t->info);
/*
* The loading routine really does not care what format the icons
* are in and we can write in any format, except IRIS RGB which
* does its own things with the filename
*/
JPEG1_dump(t->im);
close_image(t->im);
}
}
/**********************************************************************
* Check if a given name already has a thumbnail image and load the
* image if it does.
*********************************************************************/
static int
ithumb_load(Thumb_t * t)
{
IPTR im = 0;
char buf[1024], info[128], name[128];
FILE *fp;
int isthumi;
/* complete filename */
strcat(strcpy(buf, THUMBDIR), t->name);
isthumi = (fp = fopen(buf, "r")) &&
fgets(buf, 1024, fp) &&
strncmp(buf, THUMBSIG, 3) == 0;
if (isthumi)
{
register char *p = name;
Readline(p, fp);
p = info;
Readline(p, fp);
im = load_image_fp(fp, t->name);
close_image(im);
}
if (im && im->ok)
{
free_thumbnail_raster(t);
t->info = strdup(info);
t->im = im;
}
M_info("ThumbILoad", "%s Loading %s", t->im ? "OK" : "Bad", t->name);
errno = 0;
return t->im ? 0 : -1;
}
/********************************************************************
* Get thumbnail image size
********************************************************************/
static void
get_tni_size(IPTR im, int *w, int *h)
{
if (fitexact)
{
*w = ICON_W;
*h = ICON_H;
}
else
{
double xs = ICON_W / im->w;
double ys = ICON_H / im->h;
xs = Min(xs, ys);
*w = xs * im->w;
*h = xs * im->h;
}
}
/**********************************************************************
* Given a filename, generate a thumbnail image out of it
**********************************************************************/
static int
ithumb_gen(Thumb_t * t)
{
IPTR itmp;
int w, h;
if ((itmp = load_image(t->name)))
{
/* might delete the wrong thing */
del_text();
del_marking();
free_thumbnail_raster(t);
t->info = strdup(image_vital_info(itmp));
get_tni_size(itmp, &w, &h);
img_scale(itmp, h, w, tni_quality, fitexact, 0);
t->im = itmp;
}
return t->im ? 0 : -1;
}
/*************************************************************************
* Given a filename, either load, if exists already, or generate
* a thumbnail image if not or if the thumbnail is older than the
* image file
*************************************************************************/
static void
ithumb_update(Thumb_t * t, int update)
{
char tfbuf[PATH_MAX];
if (!update)
{
ithumb_load(t);
return;
}
strcat(strcpy(tfbuf, THUMBDIR), t->name);
/* generate a new thumbnail if it is older than image or there is none */
if ((f_mtime(t->name) > f_mtime(tfbuf)) || (!t->info && ithumb_load(t) < 0))
{
if (ithumb_gen(t) >= 0)
ithumb_dump(t);
}
}
#include <sys/types.h>
#include <sys/stat.h>
static int
make_ithumb_dir(const char *dir)
{
/*
* If dir already exists and permission is OK, don't have to try making
* thumbnail dir
*/
if (access(THUMBDIR, F_OK | X_OK) && mkdir(THUMBDIR, 0777))
{
Bark("MakeIconDir", "can't create %s%s", dir, THUMBDIR);
return -1;
}
errno = 0;
return 0;
}
/********************************************************
* Remove thumbnails whose orignal images no longer exist
********************************************************/
static void
clean_thumbnail_dir(const char *dir, int br)
{
char tdir[PATH_MAX + 1];
Dirlist *dl, *dls;
Thumb_t *t;
int n;
strcat(strcpy(tdir, dir), THUMBDIR);
if (!is_valid_dir(tdir))
return;
show_busy("DelIcons ...");
push_dir();
/* Read dir list */
chdir(tdir);
dl = get_dir_list(tdir, 0, &n, 1);
if (dl && n > 0)
{
for (dls = dl + n; dl < dls; dl++)
{
/* check for matches among original filenames */
for (t = ithumb[br]; t && strcmp(t->name, dl->name);
t = t->next)
;
if (!t)
{
M_info("DelIcon", "Removing %s", dl->name);
remove(dl->name);
}
}
}
pop_dir();
errno = 0;
end_busy();
}
/*** When deleting orignal files, this routine will called */
void
del_thumbnail(const char *dir, const char *file)
{
char cdir[PATH_MAX + 128];
char cfile[128];
if (file && *file)
{
if (file[0] == '/')
{
split_fname(cdir, cfile, file);
fix_dir_tail(cdir);
strcat(strcat(cdir, THUMBDIR), cfile);
}
else
{
make_complete_dir(dir, cdir);
strcat(strcat(cdir, THUMBDIR), file);
}
M_info("DelThumbI", "removing %s", cdir);
if (access(cdir, F_OK) == 0 && remove(cdir))
M_warn("DelThumbI", cdir);
}
}
/******************************************************************
* Default images. Must be called from within fill_ibroser to
* suppress info reporting
******************************************************************/
static void
load_default_thumbi(void)
{
if (!dir_image)
dir_image = load_image(get_HELPFile("dir80x60.icon"));
if (!unknown)
unknown = load_image(get_HELPFile("unknown.icon"));
}
static Thumb_t *
current_offset(int br)
{
register int off = offset[br], i = 0;
register Thumb_t *t = ithumb[br];
while (i < off)
{
t = t->next;
++i;
}
return t;
}
static const char *
icon_label(Thumb_t * t)
{
static char buf[PATH_MAX];
strcpy(buf, t->name);
/*
* limit filename to MAXFLSHOWN chars so that it does not wander into
* next/previous icon space
*/
if (strlen(buf) > MAXFLSHOWN)
buf[MAXFLSHOWN] = '\0';
return strcat(strcat(buf, "\n"), t->info ? t->info : t->ifmt);
}
/*********************************************************************
* Assign icon bitmap and labels from thumbnail images
*********************************************************************/
static void
assign_icon(FL_OBJECT * ob, Thumb_t * t)
{
IPTR im;
if ((im = t->im))
{
fl_set_icon_bitmap(ob, im->w, im->h, im->mraster, 1);
}
else
{
/*
* dir entry either has t->info filled or ifmt == "Dir". t->info is
* freed after delall, so need check both
*/
if (T_ISDIR(t))
{
if (dir_image)
fl_set_icon_bitmap(ob, dir_image->w,
dir_image->h, dir_image->mraster, 1);
}
else
{
if (unknown)
fl_set_icon_bitmap(ob, unknown->w,
unknown->h, unknown->mraster, 1);
}
}
/* Record filename for call back */
fl_set_icon_info(ob, t->name);
fl_set_object_label(ob, icon_label(t));
fl_set_icon(ob, t->selected);
}
static void
show_current_number(int br)
{
int lastn;
char buf[128];
/* last thumbnail sequence number */
lastn = (offset[br] + MAXTNI) > itotal[br] ?
itotal[br] : (offset[br] + MAXTNI);
sprintf(buf, "%d of %d", lastn, itotal[br]);
fl_set_object_label(nfiles[br], buf);
}
/******************************************************************
* Assign icons and associated files. If update is positive, we
* generate thumbnails if it is not already there. If update
* is negative, do nothing. This implements paging without updating
* icons
******************************************************************/
static int
fill_ibrowser(int br, int update)
{
Thumb_t *t = ithumb[br];
int i, off = offset[br], k = 0, total = itotal[br];
FL_OBJECT **ic = icons[br];
int savereport = report_level;
report_level = -1;
show_busy("");
load_default_thumbi();
/* skip till we get to the offset */
while (k < off)
{
t = t->next;
k++;
}
/* block until we are done */
IB_BLOCK;
/* if update, it looks better if we update as we go */
if (update <= 0)
{
fl_freeze_form(ibrowser[br]);
fl_redraw_object(dnbox[br]);
}
/* print current and total icons */
show_current_number(br);
for (i = 0; (i + off) < total && i < MAXTNI && t; i++, t = t->next)
{
show_busy("");
/* directory always has info filled */
if ((!t->info && update >= 0) || update > 0)
ithumb_update(t, update);
/* if not updating, the whole box is cleared */
if (update > 0)
fl_hide_object_only(ic[i]);
assign_icon(ic[i], t);
fl_show_object(ic[i]);
}
for (; i < MAXTNI; i++)
fl_hide_object_only(ic[i]);
IB_UNBLOCK;
if (update <= 0)
fl_unfreeze_form(ibrowser[br]);
report_level = savereport;
end_busy();
return 0;
}
/***********************************************************************
* List all dir entries and load thumbnail images
***********************************************************************/
static int
load_dir(const char *dname, int rescan)
{
int n, br, cached, i;
Thumb_t *t;
Dirlist *dl, *dlh;
const char *ifmt;
long rlines;
/* check if this dir is cached */
show_busy("Reading Dir ...");
if (!(dl = dlh = get_dir_list(dname, pat, &n, rescan)))
{
end_busy();
return -1;
}
cached = alloc_browser(dname, &br);
end_busy();
if ((cached && !rescan) || br < 0)
return br;
#ifdef S100x80
create_browser_100x80(br);
#else
create_browser_80x60(br);
#endif
rlines = progress_report("Checking Files ...", n);
push_dir();
chdir(dname);
if (rescan)
{
/* signify all bitmaps are freed */
for (i = 0; i < 15; i++)
fl_set_icon_bitmap(icons[br][i], ICON_W, ICON_H, 0, 1);
/* now free it */
free_ibrowser(br);
}
/* generate thumbnail image dir */
make_ithumb_dir(dname);
/* Generate thumbnail images and load it. */
for (i = 0, dlh = dl + n; dl < dlh; dl++, i++)
{
REPORT(i, rlines);
/* how to handle directories */
if (dl->type == FT_DIR)
{
if (dl->name[0] != '.')
{
t = ithumb_alloc();
t->info = strdup("Directory");
t->name = strdup(dl->name);
t->ifmt = strdup("Dir");
append_ibrowser(br, t);
}
}
else
{
if ((ifmt = is_image_file(dl->name)))
{
t = ithumb_alloc();
t->name = strdup(dl->name);
t->ifmt = strdup(ifmt);
append_ibrowser(br, t); /* a place holder */
}
}
}
remove_progress_report();
fl_set_object_label(title[br], contract_dirname(dname, 28));
(itotal[br] < MAXTNI ? fl_hide_object_only : fl_show_object) (pager[br]);
/* default is not to generate thumbnail images if not exist */
fill_ibrowser(br, rescan);
pop_dir();
return br;
}
/*** Given a dirname, expand and append / */
static void
make_complete_dir(const char *dirname, char *okdir)
{
int i;
/* generate a complete dirname */
if (dirname && *dirname)
strcpy(okdir, dirname);
else
getcwd(okdir, PATH_MAX - 2);
fix_dirname(okdir);
i = strlen(okdir);
/* append / to it */
if (okdir[i - 1] != '/')
{
okdir[i] = '/';
okdir[i + 1] = '\0';
}
}
/**********************************************************************
* Global entry point
**********************************************************************/
int
ibrowse_dir(const char *dirname)
{
int br;
fl_deactivate_all_forms();
make_complete_dir(dirname, complete_dir);
push_dir(); /* save current dir */
chdir(complete_dir);
if ((br = load_dir(complete_dir, 0)) < 0)
{
Bark("IbrowseDir", complete_dir);
fl_activate_all_forms();
return -1;
}
bit_show_form(ibrowser[br], FL_PLACE_SIZE, 0, complete_dir);
if (auto_remove_thumbnail)
clean_thumbnail_dir(complete_dir, br);
pop_dir();
fl_activate_all_forms();
return 0;
}
/* ARGSUSED */
static void
finish_it(FL_OBJECT * ob, long q)
{
bit_hide_form(ibrowser[q]);
}
/*****************************************************************
* Generate and write a thumbnail given an image. Return 1 if new
*****************************************************************/
/* ARGSUSED */
static Thumb_t *
img_update_icon(FL_OBJECT * ob, IPTR im, long br)
{
Thumb_t *t;
const char *f = im->ifile;
int oldreport = report_level;
IPTR itm;
int w, h;
for (t = ithumb[br]; t && strcmp(t->name, f); t = t->next)
;
if (t && (!t->info || !t->im))
{
report_level = -1;
itm = img_dup(im);
/* this will prevent fill_ibrowser from reading disk file */
t->info = strdup(image_vital_info(itm));
get_tni_size(itm, &w, &h);
show_busy("Scaling ...");
img_scale(itm, h, w, tni_quality, fitexact, 0);
t->im = itm;
ithumb_dump(t);
report_level = oldreport;
t->selected = 0;
end_busy();
return (t->im && t->im->mraster) ? t : 0;
}
return 0;
}
/*******************************************************************
* Handle mouse click on the thumbnail image. Load and display.
* of course if its thumbnail has not been made, make it
********************************************************************/
static void
handle_it(FL_OBJECT * ob, long q)
{
IPTR im;
const char *fname = fl_get_icon_info(ob);
Thumb_t *t;
if (!fl_get_icon_dblclk(ob))
{
register int i;
register FL_OBJECT **ic = icons[q];
t = current_offset(q);
for (i = 0; i < MAXTNI && ob != ic[i]; i++)
t = t->next;
if (t)
t->selected = fl_get_icon(ob);
return;
}
/* handle double click */
push_dir();
chdir(lastdir[q]);
if (is_valid_dir(fname))
{
ibrowse_dir(fname);
pop_dir();
return;
}
/*
* must prevent page fliping or any other activities as loading JPEG (and
* others ) is not re-entrant
*/
fl_deactivate_all_forms();
del_text();
del_marking();
if ((im = load_image(fname)))
{
free_image(imgptr);
imgptr = im;
imgptr->io->display(im, 0, always_clear);
if (imgptr->more)
handle_multi_images(imgptr);
/* returns icon if new */
if ((t = img_update_icon(ob, imgptr, q)))
{
fl_hide_object_only(ob);
assign_icon(ob, t);
fl_show_object(ob);
}
}
fl_activate_all_forms();
pop_dir();
}
/**********************************************************************
* New directory
**********************************************************************/
/* ARGSUSED */
static void
newdir_cb(FL_OBJECT * ob, long br)
{
const char *ndir;
static char dbuf[PATH_MAX];
char prom[1024];
getcwd(dbuf, PATH_MAX - 2);
sprintf(prom, "NewDir (current %s)", dbuf);
if ((ndir = getstring(prom, "", 1)) && *ndir)
{
strcpy(dbuf, ndir);
fix_dirname(dbuf);
if (is_valid_dir(dbuf))
{
ibrowse_dir(dbuf);
}
else
{
Bark("NewDir", "Bad dir %s", dbuf);
}
}
}
/***********************************************************************
* Walk Up one level
**********************************************************************/
/* ARGSUSED */
static void
parent_cb(FL_OBJECT * ob, long br)
{
char dbuf[PATH_MAX], *p;
strcpy(dbuf, lastdir[br]);
/* lastdir always has the trailing /. zap the tail */
p = strrchr(dbuf, '/');
*p = '\0';
p = strrchr(dbuf, '/');
*p = '\0';
ibrowse_dir(dbuf);
}
/*************************************************************************
* update all visible icons
**************************************************************************/
/* ARGSUSED */
static void
update_cb(FL_OBJECT * ob, long br)
{
fill_ibrowser(br, 1);
}
/*************************************************************************
* Rescan directory and update all visible icons
*************************************************************************/
/* ARGSUSED */
static void
rescan_cb(FL_OBJECT * ob, long br)
{
char dbuf[PATH_MAX];
IB_BLOCK;
/* can't use lastdir[br] directly as load_dir will do a free lastdir[br] */
strcpy(dbuf, lastdir[br]);
load_dir(dbuf, 1);
/* also clean up the thumnail directory */
clean_thumbnail_dir(dbuf, br);
IB_UNBLOCK;
}
/*********************************************************************
* Save the current state of marking information
*********************************************************************/
static void
save_mark_info(int br)
{
int i = 0, off = offset[br];
Thumb_t *t = ithumb[br];
FL_OBJECT **ic = icons[br];
/* remember the marking info */
while (i < off)
{
t = t->next;
i++;
}
for (i = 0; i < MAXTNI && (i + off) < itotal[br] && t; i++, t = t->next)
t->selected = fl_get_icon(ic[i]);
}
/***********************************************************************
* Paging: This turns out a little tricker than previously thought because
* no of the loading modules are re-entrant. Can't really allow next_cb
* and prev_cb to run simultanously (that is, allow it to run only if
* fill_ibrowser has finished.
**********************************************************************/
static FL_OBJECT *upb[MAXDIR], *dnb[MAXDIR];
/***********************************************************************
* Paging: up
**********************************************************************/
/* ARGSUSED*/
static void
prev_cb(FL_OBJECT * ob, long br)
{
save_mark_info(br);
IB_BLOCK;
if (offset[br] > 0)
{
if ((offset[br] -= MAXTNI) < 0)
offset[br] = 0;
fl_set_slider_value(sld[br], 1.0 - (float) offset[br] / itotal[br]);
fill_ibrowser(br, control_down ? -1 : 0);
}
IB_UNBLOCK;
}
/**********************************************************************
* Paging down
**********************************************************************/
/* ARGSUSED */
static void
next_cb(FL_OBJECT * ob, long br)
{
int lastn;
save_mark_info(br);
IB_BLOCK;
if ((offset[br] + MAXTNI) < itotal[br])
{
offset[br] += MAXTNI;
fill_ibrowser(br, control_down ? -1 : 0);
lastn = (offset[br] + MAXTNI);
lastn = (lastn > itotal[br] ? itotal[br] : lastn);
fl_set_slider_value(sld[br], 1.0 - (float) lastn / itotal[br]);
}
IB_UNBLOCK;
}
/********************************************************************
* This routine is called when the slider is RELEASED. This way, we
* can advance or backup quickly without loading all the iocns in
* between
*******************************************************************/
static void
slider_cb(FL_OBJECT * ob, long br)
{
double p = fl_get_slider_value(ob);
int leftover = (itotal[br] % MAXTNI);
int start;
IB_BLOCK;
if ((start = (1.0 - p) * (itotal[br] - leftover)) < 0)
start = 0;
offset[br] = start;
fill_ibrowser(br, 0);
IB_UNBLOCK;
}
/*************************************************************************
* Add all marked files to the file list
**************************************************************************/
/* ARGSUSED */
static void
add_cb(FL_OBJECT * ob, long br)
{
Thumb_t *p = ithumb[br];
push_dir();
chdir(lastdir[br]);
/* add all files that are selected, excluding directoies */
while (p)
{
if (p->selected && !is_valid_dir(p->name))
add_to_list(lastdir[br], p->name);
p = p->next;
}
pop_dir();
}
/************************************************************************
* Delete all marked icons
***********************************************************************/
/* ARGSUSED */
static void
del_cb(FL_OBJECT * ob, long br)
{
Thumb_t *p = ithumb[br], *q;
push_dir();
chdir(lastdir[br]);
/* if this cd fails, must not proceed, might delete the original image */
if (chdir(THUMBDIR))
{
M_err("ChDir", "%s%s", lastdir[br], THUMBDIR);
return;
}
while (p)
{
q = p->next;
if (p->selected && !is_valid_dir(p->name))
{
remove(p->name);
ithumb_del(p, br);
M_info("DeleteIcon", p->name);
}
p = q;
}
pop_dir();
/* re-fill */
fill_ibrowser(br, 0);
}
/*******************************************************
* Select all files
******************************************************/
/* ARGSUSED */
static void
mark_all(FL_OBJECT * ob, long br)
{
Thumb_t *t = ithumb[br];
while (t)
{
t->selected = 1;
t = t->next;
}
fill_ibrowser(br, 0);
}
/*******************************************************
* Unselected all files
*******************************************************/
/* ARGSUSED*/
static void
unmark_all(FL_OBJECT * ob, long br)
{
Thumb_t *t = ithumb[br];
while (t)
{
t->selected = 0;
t = t->next;
}
fill_ibrowser(br, 0);
}
/*********************************************************************
* Delete all icons, but do not update the browser
*********************************************************************/
/* ARGSUSED */
static void
delall_cb(FL_OBJECT * ob, long br)
{
char dbuf[PATH_MAX];
if (lastdir[br] && ithumb[br])
{
strcat(strcpy(dbuf, lastdir[br]), THUMBDIR);
rm_all_files(dbuf, "*");
/* warn only failed to remove */
if (access(dbuf, F_OK) == 0 && rmdir(dbuf))
M_warn("DelAll", "%s not removed", dbuf);
free_ibrowser_thumbnail_image(br);
fill_ibrowser(br, 0);
}
}
/********************************************************************
* External bindings that operate on the ORIGINAL images
*****************************************************************{**/
typedef struct
{
char *label; /* button label */
char *shcmd; /* shell command */
int multiargs; /* true if accepts multi args */
int confirm; /* if confirm before doing it */
}
ib_ext_bind;
static ib_ext_bind ibext[MAXDIR], *ibb;
static void
copy_ibbind(ib_ext_bind * to, ib_ext_bind * from)
{
to->multiargs = from->multiargs;
to->confirm = from->confirm;
StrReDup(to->label, from->label ? from->label : "?");
StrReDup(to->shcmd, from->shcmd ? from->shcmd : "");
}
static int
no_binding(ib_ext_bind * b)
{
return (!b->shcmd || !*b->shcmd);
}
/****************************************************************
* Read bindings from disk or copy last binding. This routine
* is guranteed to be called only once for each br
****************************************************************/
static const char *ibbindfile = "IbrowserBind";
#define READFMT "%[^_]_ %[^_]_ %d %d"
static void
load_ibext(int br)
{
FILE *fp;
ibb = ibext + br;
if ((fp = get_BITfile_fp(ibbindfile, "r")))
{
char buf[256];
int baddef;
buf[0] = '\0'; /* in case readline fails */
baddef = readline(fp, buf, sizeof(buf)) == EOF;
StrReDup(ibb->label, buf);
baddef = baddef || readline(fp, buf, sizeof(buf)) == EOF;
StrReDup(ibb->shcmd, buf);
baddef = baddef || (ibb->confirm = readpint(fp)) < 0;
baddef = baddef || (ibb->multiargs = readpint(fp)) < 0;
fclose(fp);
if (!baddef)
return;
}
/* we got here only if bad defination or no defination at all */
if (br == 0)
{
StrReDup(ibb->label, "?");
}
else
{
/* copy last binding */
copy_ibbind(ibb, ibb - 1);
}
}
/****** Write Current binding to Disk ****/
/* ARGSUSED */
static void
save_ibext(FL_OBJECT * ob, long br)
{
FILE *fp;
if (no_binding(ibb) || !(fp = get_BITfile_fp(ibbindfile, "w")))
return;
fprintf(fp, "%s\n%s\n %d %d %s\n", ibb->label, ibb->shcmd,
ibb->confirm, ibb->multiargs, "confirm multiarg");
fclose(fp);
}
/** Gather marked files into a single string ***/
static char *
gather_marked_files(int br)
{
Thumb_t *p = ithumb[br];
char *ha = 0, *oldha = "";
while (p)
{
if (p->selected)
{
ha = vstrcat(oldha, " ", p->name, (char *) 0);
if (*oldha) /* not first time */
free_vstrcat(oldha);
oldha = ha;
}
p = p->next;
}
return ha;
}
static void
free_gathered_file(char *f)
{
free_vstrcat(f);
}
/************************************************************************
* Actually execute the binding with "?" repalced by filename(s)
************************************************************************/
static int
execute_it(int br, const char *cmd)
{
int status;
/*
* protect "current directory", also it is very important to run the
* shell command in correct directory
*/
push_dir();
status = (chdir(lastdir[br]) || system(cmd));
pop_dir();
return status;
}
static int
exe_multi_arg(int br)
{
char *p, *all, *syscmd;
char shcmd[1024];
int ok;
if (!(all = gather_marked_files(br)))
{
Bark("ExecuteIbBind", "No marked files");
/* return 0 to shut up message generator */
return 0;
}
Strncpy(shcmd, ibb->shcmd, sizeof(shcmd) - 1);
/*
* generate complete shell command on the fly, replacing '?' with
* applicable file names
*/
if ((p = strchr(shcmd, '?')))
*p++ = '\0';
syscmd = vstrcat(shcmd, " ", all, p, (char *) 0);
/* no use for the marked file list, free it */
free_gathered_file(all);
M_info("ExecuteIbBind", "CMD=%s", syscmd);
ok = execute_it(br, syscmd) == 0;
free_vstrcat(syscmd);
return !ok;
}
/**********************************************************************
* Run a loop over all marked files. Abort on first failure
**********************************************************************/
static int
exe_single_arg(int br)
{
Thumb_t *t = ithumb[br];
char shcmd[1024], *p, *syscmd;
int ok = 1;
/* breaking up the command */
Strncpy(shcmd, ibb->shcmd, sizeof(shcmd) - 1);
if ((p = strchr(shcmd, '?')))
*p++ = '\0';
while (t && ok)
{
if (t->selected)
{
syscmd = vstrcat(shcmd, " ", t->name, p, (char *) 0);
ok = execute_it(br, syscmd) == 0;
free_vstrcat(syscmd);
}
t = t->next;
}
return !ok;
}
static void
execute_ibext(int br)
{
ibb = ibext + br;
if (ibb->confirm && !yes_no("ExecuteBinding", ibb->shcmd, "", 0))
return;
if ((ibb->multiargs ? exe_multi_arg : exe_single_arg) (br))
Bark("ExecuteBinding", "Something is wrong");
}
static void get_ibext(int);
static void
ext_bind_cb(FL_OBJECT * ob, long br)
{
ibb = ibext + br;
switch (fl_get_button_numb(ob))
{
case 1: /* right mouse */
get_ibext(br);
break;
case 2: /* middle mouse */
case 3: /* left mouse */
/* if no binding is defined, define it now */
(no_binding(ibb) ? get_ibext : execute_ibext) (br);
break;
}
}
static FL_FORM *ibbind;
static FL_OBJECT *ibbut[MAXDIR];
static void create_form_ibbind(void);
static void
show_ibbut(int br)
{
ibb = ibext + br;
if (!ibb->shcmd || !*ibb->shcmd)
return;
fl_set_object_label(ibbut[br], ibb->label ? ibb->label : ibb->shcmd);
}
static void
get_ibext(int br)
{
short val;
ibb = ibext + br;
create_form_ibbind();
fl_deactivate_all_forms();
bit_show_form(ibbind, FL_PLACE_CENTER, 0, "");
while (bit_qread(&val) != F1KEY || !val)
;
bit_hide_form(ibbind);
show_ibbut(br);
fl_activate_all_forms();
}
/* ARGSUSED */
static void
get_cmd_cb(FL_OBJECT * ob, long q)
{
const char *p = fl_get_input(ob);
if (p && *p)
{
StrReDup(ibb->shcmd, p);
}
else
{
free(ibb->shcmd);
ibb->shcmd = 0;
}
}
/* ARGSUSED */
static void
get_lab_cb(FL_OBJECT * ob, long q)
{
const char *p = fl_get_input(ob);
StrReDup(ibb->label, (p && *p) ? p : "?");
}
static void
get_ibopt_cb(FL_OBJECT * ob, long q)
{
if (q == 0)
ibb->confirm = fl_get_button(ob);
else
ibb->multiargs = fl_get_button(ob);
}
/* ARGSUSED */
static void
ibbind_done(FL_OBJECT * ob, long q)
{
fl_qenter(F1KEY, 1);
}
static void
create_form_ibbind(void)
{
FL_OBJECT *obj;
if (ibbind)
return;
ibbind = fl_bgn_form(FL_NO_BOX, 270.0, 180.0);
obj = fl_add_box(FL_UP_BOX, 0.0, 0.0, 270.0, 180.0, "");
fl_set_object_color(obj, 12, 47);
obj = fl_add_box(FL_DOWN_BOX, 10.0, 10.0, 250.0, 160.0, "");
fl_set_object_color(obj, 12, 47);
obj = fl_add_button(FL_HB, 0.0, 0.0, 270.0, 180.0, "");
fl_set_call_back(obj, help_cb, HELP_IBBIND);
obj = fl_add_input(FL_NORMAL_INPUT, 90.0, 120.0, 160.0, 30.0, "Label");
fl_set_object_color(obj, 47, 52);
fl_set_object_lsize(obj, 10.000000);
fl_set_input_return(obj, 1);
fl_set_call_back(obj, get_lab_cb, 0);
obj = fl_add_input(FL_NORMAL_INPUT, 90.0, 90.0, 160.0, 30.0, "Command");
fl_set_object_color(obj, 47, 52);
fl_set_object_lsize(obj, 10.000000);
fl_set_input_return(obj, 1);
fl_set_call_back(obj, get_cmd_cb, 0);
obj = fl_add_roundbutton(FL_PB, 20.0, 50.0, 30.0, 30.0, "Confirm");
fl_set_object_color(obj, 47, 3);
fl_set_object_lsize(obj, 10.000000);
fl_set_call_back(obj, get_ibopt_cb, 0);
obj = fl_add_roundbutton(FL_PB, 110.0, 50.0, 30.0, 30.0, "MultiArgs");
fl_set_object_color(obj, 47, 3);
fl_set_object_lsize(obj, 10.000000);
fl_set_call_back(obj, get_ibopt_cb, 1);
obj = fl_add_button(FL_NB, 50.0, 20.0, 55.0, 25.0, "Save");
fl_set_call_back(obj, save_ibext, 0);
obj = fl_add_button(FL_NB, 190.0, 20.0, 55.0, 25.0, "OK");
fl_set_object_lsize(obj, 10.000000);
fl_set_call_back(obj, ibbind_done, 0);
fl_end_form();
}
/****** End of ext binding ***********}***/
/*******************************************************************
* All control functions
******************************************************************/
typedef struct
{
const char *l;
void (*cb) (FL_OBJECT *, long);
const char *scut;
int c;
}
Cb_t;
static Cb_t cbs[] =
{
{"MarkAll", mark_all, 0, FL_YELLOW},
{"UnMarkAll", unmark_all, 0, FL_GREEN},
{"Add2List", add_cb, 0, FL_GREEN},
{"Delete", del_cb, 0, FL_YELLOW},
{"DelAll", delall_cb, 0, FL_RED}
};
/***********************************************************************
* Hack to make redraw in single buffer a little nicer
***********************************************************************/
#ifndef S100x80
static void
create_browser_80x60(int br)
{
FL_OBJECT *obj;
float dx, dy, x, y;
int i, j, k;
float formw = 505.0, formh = 370.0;
if (ibrowser[br])
return;
load_ibext(br);
ibrowser[br] = fl_bgn_form(FL_UP_BOX, formw, formh);
obj = fl_add_button(FL_HB, 0, 0, formw, formh, "");
fl_set_call_back(obj, help_cb, HELP_IBR);
dnbox[br] = obj = fl_add_box(FL_DOWN_BOX, 10.0, 40.0, 470.0, 295.0, "");
pager[br] = fl_bgn_group();
/* paging */
dx = 20;
x = formw - dx - 5.;
y = 43.0;
/* down button */
dnb[br] = obj = fl_add_button(FL_NB, x, y, dx, dx, "@2");
fl_set_call_back(obj, next_cb, br);
fl_set_object_lcol(obj, FL_RED);
/* slider */
y += dx;
dy = 295 - 2 * dx - 3;
sld[br] = obj = fl_add_slider(FL_VERT_NICE_SLIDER, x, y, dx, dy, "");
fl_set_object_boxtype(obj, FL_BORDER_BOX);
fl_set_object_color(obj, FL_MAGIC1, FL_RED);
fl_set_slider_return(obj, 0);
fl_set_call_back(obj, slider_cb, br);
fl_set_slider_value(obj, 1.0);
/* up button */
y += dy;
upb[br] = obj = fl_add_button(FL_NB, x, y + 1, dx, dx, "@8");
fl_set_call_back(obj, prev_cb, br);
fl_set_object_lcol(obj, FL_RED);
fl_end_group();
y = 340;
x = 10.0;
dy = 23;
dx = 70;
nfiles[br] = obj = fl_add_text(FL_NT, x, y, dx, dy, "");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_lsize(obj, 8.0);
fl_set_object_align(obj, FL_ALIGN_CENTER);
x += dx;
dx = 40;
obj = fl_add_button(FL_NB, x, y, dx, dy, "../");
fl_set_object_lstyle(obj, FL_BOLD_STYLE);
fl_set_object_lsize(obj, 11.0);
fl_set_call_back(obj, parent_cb, br);
/* current directory: may display about 40 charactors */
x += dx;
dx = 170;
title[br] = obj = fl_add_text(FL_NT, x, y, dx, dy, "");
fl_set_object_lsize(obj, 10.0);
fl_set_object_align(obj, FL_ALIGN_CENTER);
x += dx;
dx = 65;
obj = fl_add_button(FL_NB, x, y, dx, dy, "NewDir");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, newdir_cb, br);
x += dx;
obj = fl_add_button(FL_NB, x, y, dx, dy, "Update");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, update_cb, br);
fl_set_object_color(obj, FL_MAGIC1, FL_YELLOW);
x += dx;
obj = fl_add_button(FL_NB, x, y, dx, dy, "ReScan");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, rescan_cb, br);
fl_set_object_color(obj, FL_MAGIC1, FL_YELLOW);
x = 18;
y = 265;
dx = 91;
dy = 92;
for (i = k = 0; i < 3; i++, x = 20, y -= dy)
for (j = 0; j < 5; j++, x += dx)
{
icons[br][k++] = obj =
fl_add_icon(FL_DBL_CLK_ICON, x, y, 80.0, 60.0, "");
fl_set_object_boxtype(obj, FL_NO_BOX);
fl_set_object_lsize(obj, 8.0);
fl_set_object_lstyle(obj, FL_NORMAL_STYLE);
fl_set_object_align(obj, FL_ALIGN_BOTTOM);
fl_set_call_back(obj, handle_it, br);
}
dx = 65;
dy = 25;
x = 10.0;
y = 10.0;
for (i = 0; i < sizeof(cbs) / sizeof(cbs[0]); i++, x += dx)
{
obj = fl_add_button(FL_NB, x, y, dx, dy, cbs[i].l);
fl_set_object_lsize(obj, 10.0);
fl_set_object_color(obj, FL_MAGIC1, cbs[i].c);
fl_set_call_back(obj, cbs[i].cb, br);
}
/* external bindings */
ibbut[br] = obj = fl_add_button(FL_NB, x, y, dx, dy, ibext[br].label);
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, ext_bind_cb, br);
x += dx;
obj = fl_add_button(FL_NB, 490 - dx - 8, y, dx, dy, "Close");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, finish_it, br);
fl_end_form();
}
#else
static void
create_browser_100x80(int br)
{
FL_OBJECT *obj;
float x, y, dx, dy;
int i, j, k;
float formw = 620, formh = 440;
if (ibrowser[br])
return;
load_ibext(br);
ibrowser[br] = fl_bgn_form(FL_UP_BOX, formw, formh);
obj = fl_add_button(FL_HB, 0, 0, formw, formh, "");
fl_set_call_back(obj, help_cb, HELP_IBR);
dnbox[br] = obj = fl_add_box(FL_DOWN_BOX, 10.0, 40.0, 580.0, 360.0, "");
/* pager group */
pager[br] = fl_bgn_group();
dx = 20;
x = formw - 7 - dx;
y = 43;
dnb[br] = obj = fl_add_button(FL_NB, x, y, dx, dx, "@2");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_lcol(obj, 1);
fl_set_call_back(obj, next_cb, br);
y += dx + 1;
dy = 360 - 2 * dx - 3;
sld[br] = obj = fl_add_slider(FL_VERT_NICE_SLIDER, x, y, dx, dy, "");
fl_set_object_boxtype(obj, FL_BORDER_BOX);
fl_set_object_color(obj, FL_MAGIC1, FL_RED);
fl_set_slider_return(obj, 0);
fl_set_call_back(obj, slider_cb, br);
fl_set_slider_value(obj, 1.0);
y += dy;
upb[br] = obj = fl_add_button(FL_NB, x, y, dx, dx, "@8");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_lcol(obj, 1);
fl_set_call_back(obj, prev_cb, br);
fl_end_group();
dy = 23;
y = 405;
dx = 100.0;
nfiles[br] = obj = fl_add_text(FL_NT, 10.0, y, dx, dy, "");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_lsize(obj, 8.0);
fl_set_object_align(obj, FL_ALIGN_CENTER);
obj = fl_add_button(FL_NB, 120, y, 30, dy, "../");
fl_set_object_lsize(obj, 11.0);
fl_set_object_lstyle(obj, FL_BOLD_STYLE);
fl_set_call_back(obj, parent_cb, br);
title[br] = obj = fl_add_text(FL_NT, 190.0, y, 190.0, dy, "");
fl_set_object_lsize(obj, 10.0);
fl_set_object_align(obj, FL_ALIGN_CENTER);
obj = fl_add_button(FL_NB, 400, y, 80, dy, "NewDir");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, newdir_cb, br);
obj = fl_add_button(FL_NB, 480, y, 80, dy, "Update");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, update_cb, br);
x = 20.0;
y = 310;
dx = 113.0;
dy = 113;
for (i = k = 0; i < 3; i++, x = 20.0, y -= dy)
for (j = 0; j < 5; j++, x += dx)
{
icons[br][k++] = obj = fl_add_icon(FL_DBL_CLK_ICON,
x, y, 100.0, 80.0, "");
fl_set_object_boxtype(obj, FL_NO_BOX);
fl_set_object_lsize(obj, 8.0);
fl_set_object_lstyle(obj, FL_NORMAL_STYLE);
fl_set_object_align(obj, FL_ALIGN_BOTTOM);
fl_set_call_back(obj, handle_it, br);
}
dx = 70;
dy = 25;
x = y = 10.0;
for (i = 0; i < sizeof(cbs) / sizeof(cbs[0]); i++, x += dx)
{
obj = fl_add_button(FL_NB, x, y, dx, dy, cbs[i].l);
fl_set_object_lsize(obj, 10.0);
fl_set_object_color(obj, FL_MAGIC1, cbs[i].c);
fl_set_call_back(obj, cbs[i].cb, br);
}
obj = fl_add_button(FL_NB, 520.0, 10.0, dx, dy, "Close");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, finish_it, br);
fl_set_object_align(obj, FL_ALIGN_CENTER);
fl_end_form();
}
#endif